#pragma once
#ifndef E1000_DEV_H
#define E1000_DEV_H

#include <common/defines.h>
#include "types.h"

//
// E1000 hardware definitions: registers and DMA ring format.
// from the Intel 82540EP/EM &c manual.
//

/** Registers */
#define E1000_CTL (0x00000 / 4) /* Device Control Register - RW */
#define E1000_ICR (0x000C0 / 4) /* Interrupt Cause Read - R */
#define E1000_IMS (0x000D0 / 4) /* Interrupt Mask Set - RW */

/** RX Descriptors */
#define E1000_RCTL (0x00100 / 4) /* RX Control - RW */
#define E1000_RDBAL (0x02800 / 4) /* RX Descriptor Base Address Low - RW */
#define E1000_RDTR (0x02820 / 4) /* RX Delay Timer */
#define E1000_RADV (0x0282C / 4) /* RX Interrupt Absolute Delay Timer */
#define E1000_RDH (0x02810 / 4) /* RX Descriptor Head - RW */
#define E1000_RDT (0x02818 / 4) /* RX Descriptor Tail - RW */
#define E1000_RDLEN (0x02808 / 4) /* RX Descriptor Length - RW */
#define E1000_RSRPD (0x02C00 / 4) /* RX Small Packet Detect Interrupt */

/** TX Descriptors */
#define E1000_TCTL (0x00400 / 4) /* TX Control - RW */
#define E1000_TIPG (0x00410 / 4) /* TX Inter-packet gap -RW */
#define E1000_TDBAL (0x03800 / 4) /* TX Descriptor Base Address Low - RW */
#define E1000_TDBAH (0x03804 / 4) /* TX Descriptor Base Address High - RW */
#define E1000_TDLEN (0x03808 / 4) /* TX Descriptor Length - RW */
#define E1000_TDH (0x03810 / 4) /* TX Descriptor Head - RW */
#define E1000_TDT (0x03818 / 4) /* TX Descripotr Tail - RW */

/**
 * NOTE: [E1000 3.4], page 65
 * 
 * 1. E1000_TDH  indicates the in–progress descriptor. 
 * 
 * 2. E1000_TDT is the location where software writes the first 
 * new descriptor.
 * 
 * Picture 3-4(page 66) of this queue:
 +-------+ <- base
 |       |
 +-------+
 |       |
 +-------+ <- head  --
 |       |            |
 +-------+ <-       --|<= Transmit Queue
 |       |            |
 +-------+ <- tail  --
 |       |
 +-------+
 |       |
 +-------+
 |       |
 +-------+
 |       |
 +-------+ <- base + size
 */

/** Others */
#define E1000_MTA (0x05200 / 4) /* Multicast Table Array - RW Array */
#define E1000_RA (0x05400 / 4) /* Receive Address - RW Array */

/* Device Control */
#define E1000_CTL_SLU 0x00000040 /* set link up */
#define E1000_CTL_FRCSPD 0x00000800 /* force speed */
#define E1000_CTL_FRCDPLX 0x00001000 /* force duplex */
#define E1000_CTL_RST 0x00400000 /* full reset */

/* Transmit Control */
#define E1000_TCTL_RST 0x00000001 /* software reset */
#define E1000_TCTL_EN 0x00000002 /* enable tx */
#define E1000_TCTL_BCE 0x00000004 /* busy check enable */
#define E1000_TCTL_PSP 0x00000008 /* pad short packets */
#define E1000_TCTL_CT 0x00000ff0 /* collision threshold */
#define E1000_TCTL_CT_SHIFT 4
#define E1000_TCTL_COLD 0x003ff000 /* collision distance */
#define E1000_TCTL_COLD_SHIFT 12
#define E1000_TCTL_SWXOFF 0x00400000 /* SW Xoff transmission */
#define E1000_TCTL_PBE 0x00800000 /* Packet Burst Enable */
#define E1000_TCTL_RTLC 0x01000000 /* Re-transmit on late collision */
#define E1000_TCTL_NRTU 0x02000000 /* No Re-transmit on underrun */
#define E1000_TCTL_MULR 0x10000000 /* Multiple request support */

/* Receive Control */
#define E1000_RCTL_RST 0x00000001 /* Software reset */
#define E1000_RCTL_EN 0x00000002 /* enable */
#define E1000_RCTL_SBP 0x00000004 /* store bad packet */
#define E1000_RCTL_UPE 0x00000008 /* unicast promiscuous enable */
#define E1000_RCTL_MPE 0x00000010 /* multicast promiscuous enab */
#define E1000_RCTL_LPE 0x00000020 /* long packet enable */
#define E1000_RCTL_LBM_NO 0x00000000 /* no loopback mode */
#define E1000_RCTL_LBM_MAC 0x00000040 /* MAC loopback mode */
#define E1000_RCTL_LBM_SLP 0x00000080 /* serial link loopback mode */
#define E1000_RCTL_LBM_TCVR 0x000000C0 /* tcvr loopback mode */
#define E1000_RCTL_DTYP_MASK 0x00000C00 /* Descriptor type mask */
#define E1000_RCTL_DTYP_PS 0x00000400 /* Packet Split descriptor */
#define E1000_RCTL_RDMTS_HALF 0x00000000 /* rx desc min threshold size */
#define E1000_RCTL_RDMTS_QUAT 0x00000100 /* rx desc min threshold size */
#define E1000_RCTL_RDMTS_EIGTH 0x00000200 /* rx desc min threshold size */
#define E1000_RCTL_MO_SHIFT 12 /* multicast offset shift */
#define E1000_RCTL_MO_0 0x00000000 /* multicast offset 11:0 */
#define E1000_RCTL_MO_1 0x00001000 /* multicast offset 12:1 */
#define E1000_RCTL_MO_2 0x00002000 /* multicast offset 13:2 */
#define E1000_RCTL_MO_3 0x00003000 /* multicast offset 15:4 */
#define E1000_RCTL_MDR 0x00004000 /* multicast desc ring 0 */
#define E1000_RCTL_BAM 0x00008000 /* broadcast enable */
/* these buffer sizes are valid if E1000_RCTL_BSEX is 0 */
#define E1000_RCTL_SZ_2048 0x00000000 /* rx buffer size 2048 */
#define E1000_RCTL_SZ_1024 0x00010000 /* rx buffer size 1024 */
#define E1000_RCTL_SZ_512 0x00020000 /* rx buffer size 512 */
#define E1000_RCTL_SZ_256 0x00030000 /* rx buffer size 256 */
/* these buffer sizes are valid if E1000_RCTL_BSEX is 1 */
#define E1000_RCTL_SZ_16384 0x00010000 /* rx buffer size 16384 */
#define E1000_RCTL_SZ_8192 0x00020000 /* rx buffer size 8192 */
#define E1000_RCTL_SZ_4096 0x00030000 /* rx buffer size 4096 */
#define E1000_RCTL_VFE 0x00040000 /* vlan filter enable */
#define E1000_RCTL_CFIEN 0x00080000 /* canonical form enable */
#define E1000_RCTL_CFI 0x00100000 /* canonical form indicator */
#define E1000_RCTL_DPF 0x00400000 /* discard pause frames */
#define E1000_RCTL_PMCF 0x00800000 /* pass MAC control frames */
#define E1000_RCTL_BSEX 0x02000000 /* Buffer size extension */
#define E1000_RCTL_SECRC 0x04000000 /* Strip Ethernet CRC */
#define E1000_RCTL_FLXBUF_MASK 0x78000000 /* Flexible buffer size */
#define E1000_RCTL_FLXBUF_SHIFT 27 /* Flexible buffer shift */

#define DATA_MAX 1518

/* Transmit Descriptor command definitions [E1000 3.3.3.1] */
#define E1000_TXD_CMD_EOP 0x01 /* End of Packet */
#define E1000_TXD_CMD_RS 0x08 /* Report Status */

/* Transmit Descriptor status definitions [E1000 3.3.3.2] */
#define E1000_TXD_STAT_DD 0x00000001 /* Descriptor Done */

// [E1000 3.3.3]
struct tx_desc {
    /** address of the transmit descriptor in the host memory */
    uint64 addr;
    /** length is per segment */
    uint16 length;
    /** checksum offset, should be written with 0b */
    uint8 cso;
    /** command field */
    uint8 cmd;
    /** status field */
    uint8 status;
    /** reserved, should be written with 0b */
    uint8 css;
    /** special field */
    uint16 special;
};

/* Receive Descriptor bit definitions [E1000 3.2.3.1] */
#define E1000_RXD_STAT_DD 0x01 /* Descriptor Done */
#define E1000_RXD_STAT_EOP 0x02 /* End of Packet */

// [E1000 3.2.3]
struct rx_desc {
    uint64 addr; /* Address of the descriptor's data buffer */
    uint16 length; /* Length of data DMAed into data buffer */
    uint16 csum; /* Packet checksum */
    uint8 status; /* Descriptor status */
    uint8 errors; /* Descriptor Errors */
    uint16 special;
};

extern void e1000_init(uint32 *xregs);

#endif // E1000_DEV_H
